#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<errno.h>
#include<stdbool.h>
#include<ctype.h>
#include<sys/stat.h>
#include<fcntl.h>

#define SIZE 1024
#define SEG " "
int lastcode = 0;

char commandline[SIZE];
char* argv[SIZE];

//重定向类型枚举声明表示
enum redir_type
{
    NoneRedir,
    stdinRedir,
    stdoutRedir,
    AppendRedir
};

enum redir_type rtype = NoneRedir;

//文件名，直接赋值地址
char* filename = NULL;
//宏函数，取文件名
//#define GetFilename(search) do {while(isspace(search)) search++;}while(0)
//#define GetFilename(buf, pos) do{while(isspace(buf[pos])) pos++;}while(0)
#define GetFilename(buf,pos) do{ while(isspace(buf[pos])) pos++; }while(0)

char* getUSER()
{
    char* ret = getenv((char*)"USER");
    
    return ret;
}

char* getHOSTNAME()
{
    const char* buf = (char*)"HOSTNAME";
    char* ret = getenv(buf);
    
    return ret;
}

char* getPWD()
{
    char buf[SIZE];
    char* ret = getcwd(buf, SIZE);
    
    return ret;
}

void checkredir()
{   
    //多次调用shell
    rtype = NoneRedir;
    filename = NULL;
    //printf("%s %d\n", commandline, strlen(commandline));
    int i = strlen(commandline) - 1;
    while(i >= 0)
    {
        if(commandline[i] == '>')
        {
            if(commandline[i - 1] == '>')
            {
                rtype = AppendRedir;
                commandline[i - 1] = 0;
                while(isspace(commandline[i + 1]))
                {
                    i++;
                }
                //i++;
                //GetFilename(commandline,i);
                //printf("i = %d\n", i);
                filename = commandline + i;
                //printf("%s\n", filename);
                break;
            }
            else 
            {
                //printf("this is >\n");
                rtype = stdoutRedir;
                commandline[i - 1] = 0;
                //while(isspace(commandline[i + 1]))
                //{
                //    i++;
                //}
                //重定向符号的后一位！！！！！
                i++;
                GetFilename(commandline,i);
                //printf("i = %d\n", i);
                filename = commandline + i;
                //printf("%s\n", filename);
                break;
            }
        }
        else if(commandline[i] == '<')
        {
            rtype = stdinRedir;
            commandline[i - 1] = 0;
            i++;
            GetFilename(commandline, i);
            //printf("i = %d\n",i);
            filename = commandline + i;
            break;
        }
        else 
        {
            i--;
        }
    }


}

void CommandLinePrompt()
{
    //命令行提示符与输入
    printf("[%s@%s %s]$", getUSER(), getHOSTNAME(), getPWD());
    fgets(commandline, SIZE, stdin);
    int i = strlen(commandline);
    commandline[i - 1] = 0;
    //printf("test: %s\n", commandline);
}

void Interative(int i)
{
    //执行
    pid_t pid = fork();
    if(pid == 0)
    {
        //改变子进程的文件流
        int fd = -1;
        //判断是否为重定向，改变文件流
        if(rtype == stdinRedir)
        {
            fd = open(filename, O_RDONLY);
            dup2(fd, 0);
        }
        else if(rtype == stdoutRedir)
        {
            fd = open(filename, O_WRONLY|O_TRUNC);
            dup2(fd, 1);
        }
        else if(rtype == AppendRedir)
        {
            fd = open(filename, O_WRONLY|O_APPEND);
            dup2(fd, 1);
        }

        if(strcmp(argv[0], "ls") == 0)
        {
            //彩色ls
            argv[i - 1] = (char*)"--color";
            argv[i] = NULL;
        }

        //printf("%s\n", filename);
        //printf("%d\n", fd);
        //printf("%d\n", rtype);
        
        execvp(argv[0], argv);
    }
    
    //判断子进程执行情况
    int status = 0;
    waitpid(pid, &status, 0);
    if(!WIFEXITED(status))
    {
        lastcode = WEXITSTATUS(status);
        printf("%s\n", strerror(WEXITSTATUS(status)));
    }
}

bool checkbuildcommand()
{
    //内建命令，枚举处理
    //cd命令
    if(strcmp(argv[0], "cd") == 0)
    {
        if(argv[1])
        {
            int ret = chdir(argv[1]);
            if(ret == -1)
            {
                //获取退出码
                printf("%s\n", strerror(errno));
            }
        }
        else
        {
            const char* hostname = getenv("HOME");
            chdir(hostname);
        }

        return true;
    }
    else if(strcmp(argv[0], "export") == 0)
    {
        putenv(argv[1]);

        return true;
    }
    else if(strcmp(argv[0], "echo") == 0)
    {
        //输出环境变量内容
        if(argv[1][0] == '$')
        {
            if(argv[1][1] == '?')
            {
                //查看最近一次进程的退出码
               printf("%d\n", lastcode);
               lastcode = 0;
            }
            else 
            {
                //第二个字符串首地址+1
                char* env = getenv((argv[1]+1));
                if(env)//环境变量存在
                {
                    printf("%s\n", env);
                }
            }
        }
        else 
        {
            int i = 0;
            char out[SIZE] = {0};
            int len = 0;
            for(i = 1; argv[i]; i++)
            {
                //多个参数，漏掉空格
                if(i > 1)
                {
                    out[strlen(out)] = ' ';
                    len++;
                }
                
                //覆盖掉了
                snprintf(out + len, SIZE, "%s", argv[i]);
                
                if(argv[i])
                    len += strlen(argv[i]);
            }
            //printf("%s\n", out);
            puts(out);
        }

        return true;
    }
    else 
    {
        ;
    }
    
    return false;
}

int splitcommandline()
{
    //分割命令行参数
    int i = 0;
    //此处commandline已被分割
    //char tmp[SIZE] = {0};
    //strcpy(tmp, commandline);
    //char* part = strtok(tmp, SEG);
    char* part = strtok(commandline, SEG);
    argv[i++] = part;
    while(part)
    {
        //先取值赋值再判断
        part = strtok(NULL, SEG);
        argv[i++] = part;
    }
    //printf("%s\n", commandline);

    return i;
}

void ExeCute(int i)
{
    //先与命令行参数分割
    //checkredir();

    Interative(i);
}

int main()
{
    while(1)
    {
        //命令行提示符
        CommandLinePrompt();
       
        //先粗力度分割，先判断是否含有重定向
        checkredir(); 

        //分割命令行参数
        int i = splitcommandline();

        //处理内建命令
        if(checkbuildcommand())
        {
            continue;
        }

        //执行
        ExeCute(i);
    }

    return 0;
}
